/*MyPhotoIndex Free Photo Organizer <www.myphotoindex.com>
  Copyright (C) <2006-2009>  <Asaf Yarkoni asaf@yarkoni.net>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
using System;
using System.Collections.Generic;
using System.Text;
using MyPhotoIndex.Utilities;
using MyPhotoIndex.Utilities.Types;
using System.Data;
using MyPhotoIndex.Interfaces;
using MyPhotoIndex.Interfaces.Tagging;
using MyPhotoIndex.Plugins.DataProviders;

namespace MyPhotoIndex.ImageBank
{
    [Serializable]
    public class ImageTagCollection : Singleton<ImageTagCollection>, IImageTagCollection
    {
        public event EventHandler ImageTagCollectionLoaded;

        private List<IImageTag> m_duplicateTags = new List<IImageTag>();

        private Dictionary<int, IImageTag> m_tagMap = new Dictionary<int, IImageTag>();
        private List<IImageTag> m_tagList;

        private ITagsDataAccessProvider m_dataAccessProvider;

        public void Load(ITagsDataAccessProvider dataAccessProvider)
        {
            m_dataAccessProvider = dataAccessProvider;

            m_tagList = null;
            m_tagMap.Clear();
            m_duplicateTags.Clear();
            BuildTagTree(Tags);

            if (ImageTagCollectionLoaded != null)
            {
                ImageTagCollectionLoaded.Invoke(this, EventArgs.Empty);
            }
        }

        public IImageTag this[int id]
        {
            get
            {
                return m_tagMap[id];
            }
        }

        public List<IImageTag> Tags
        {
            get
            {
                if (m_tagList == null)
                {
                    lock (this)
                    {
                        if (m_tagList == null)
                        {
                            m_tagList = new List<IImageTag>();
                            DataTable tagsDataTable = m_dataAccessProvider.GetImageTags();

                            foreach (DataRow tagRow in tagsDataTable.Rows)
                            {
                                IImageTag tag = BuildTagFromDataRow(tagRow);
                                if (tag.IsRoot == true)
                                {
                                    BuildTagsTree(tag, tagsDataTable);
                                    m_tagList.Add(tag);
                                }
                            }

                            m_tagList.Sort(new Comparison<IImageTag>(CompareNodes));
                        }
                    }
                }

                return m_tagList;
            }
        }

        private int CompareNodes(IImageTag a, IImageTag b)
        {
            return a.Name.CompareTo(b.Name);
        }

        public IImageTag GetTag(int tagId)
        {
            return GetTagInformation(Tags, tagId);
        }

        private IImageTag GetTagInformation(List<IImageTag> tags, int tagId)
        {
            foreach (IImageTag tagInfo in tags)
            {
                if (tagInfo.Id == tagId)
                {
                    return tagInfo;
                }

                IImageTag tag = GetTagInformation(tagInfo.ChildTags, tagId);
                if (tag != null)
                {
                    return tag;
                }
            }

            return null;
        }

        private void BuildTagsTree(IImageTag tag, DataTable tagsDataTable)
        {
            foreach (DataRow tagRow in tagsDataTable.Rows)
            {
                if (tag.Id == Convert.ToInt32(tagRow[(int)TagsDataSourceColumnId.Parent]))
                {
                    IImageTag childTag = BuildTagFromDataRow(tagRow);
                    childTag.Parent = tag;
                    tag.ChildTags.Add(childTag);

                    BuildTagsTree(childTag, tagsDataTable);
                }
            }
        }

        private IImageTag BuildTagFromDataRow(DataRow tagRow)
        {
            IImageTag tag = new ImageTag(tagRow);

            return tag;
        }

        public IImageTag Create(IImageTag parent, string name)
        {
            ImageTag tag = new ImageTag();
            tag.Name = name;
            tag.Parent = parent;
            tag.Offset = (parent != null)?parent.ChildTags.Count:-1;
            tag.ReferenceDate = DateTime.Now;

            return tag;
        }

        public void SaveTag(IImageTag tag)
        {
            tag.Name = tag.Name.Trim();
            DataTable tagsDataTable = m_dataAccessProvider.GetImageTags();
            DataRow tagDataRow = null;
            bool newTag = false;

            if (tag.Id <= 0)
            {
                newTag = true;
                tagDataRow = tagsDataTable.Rows.Add(new Object[]{-1, (tag.Parent != null) ? tag.Parent.Id : -1, 
                                tag.Offset, tag.Name, tag.Description,
                                   ThumbnailUtilities.ThumbnailToByteArray(tag.Thumbnail), String.IsNullOrEmpty(tag.Keyword) ? null : tag.Keyword, tag.PhysicalFolder, tag.ReferenceDate});
            }
            else
            {
                tagDataRow = tagsDataTable.Rows.Find(new object[] { tag.Id });
                tagDataRow[(int)TagsDataSourceColumnId.Name] = tag.Name;
                tagDataRow[(int)TagsDataSourceColumnId.Parent] = (tag.Parent != null) ? tag.Parent.Id : -1;
                tagDataRow[(int)TagsDataSourceColumnId.Offset] = tag.Offset;
                tagDataRow[(int)TagsDataSourceColumnId.Description] = tag.Description;
                tagDataRow[(int)TagsDataSourceColumnId.Thambnail] = ThumbnailUtilities.ThumbnailToByteArray(tag.Thumbnail);
                if (String.IsNullOrEmpty(tag.Keyword) == true)
                {
                    tagDataRow[(int)TagsDataSourceColumnId.Keyword] = null;
                }
                else
                {
                    tagDataRow[(int)TagsDataSourceColumnId.Keyword] = tag.Keyword;
                }

                tagDataRow[(int)TagsDataSourceColumnId.PhysicalFolder] = tag.PhysicalFolder;
                tagDataRow[(int)TagsDataSourceColumnId.ReferenceDate] = tag.ReferenceDate;
          
            }

            if (tag.Thumbnail == null)
            {
                tagDataRow[(int)TagsDataSourceColumnId.Thambnail] = DBNull.Value;
            }

            m_dataAccessProvider.UpdateDataTable(tagsDataTable, false, true);
            tagDataRow.AcceptChanges();
            tag.Id = Convert.ToInt32(tagDataRow[(int)TagsDataSourceColumnId.Id]);

            if (tag.Parent != null &&
                tag.Parent.ChildTags.Contains(tag) == false)
            {
                tag.Parent.ChildTags.Add(tag);
            }
            else
            {
                if (newTag == true)
                {
                    m_tagList.Add(tag);
                }
            }

            m_tagMap.Clear();
            m_duplicateTags.Clear();
            BuildTagTree(m_tagList);
        }

        public void RemoveTag(IImageTag tag)
        {
            RemoveTagInternal(tag);

            if (tag.Parent != null)
            {
                tag.Parent.ChildTags.Remove(tag);
            }
            else
            {
                m_tagList.Remove(tag);
            }

            m_tagMap.Clear();
            m_duplicateTags.Clear();
            BuildTagTree(m_tagList);
        }

        private void RemoveTagInternal(IImageTag tag)
        {
            foreach (IImageTag childTag in tag.ChildTags)
            {
                RemoveTagInternal(childTag);
            }

            DataTable tagsDataTable = m_dataAccessProvider.GetImageTags();
            DataRow tagDataRow = tagsDataTable.Rows.Find(new Object[] { tag.Id });
            if (tagDataRow == null)
            {
                return;
            }

            tagDataRow.Delete();
            m_dataAccessProvider.UpdateDataTable(tagsDataTable, false);
        }

        private void BuildTagTree(List<IImageTag> tagList)
        {
            foreach (IImageTag tag in tagList)
            {
                foreach (IImageTag tempTag in m_duplicateTags)
                {
                    if (String.Compare(tempTag.Name, tag.Name, true) == 0)
                    {
                        tag.UniqueName = false;
                        tempTag.UniqueName = false;
                    }
                }

                m_tagMap.Add(tag.Id, tag);
                m_duplicateTags.Add(tag);
                if (tag.ChildTags != null)
                {
                    BuildTagTree(tag.ChildTags);
                }
            }
        }

        public List<int> GetTagList(int tagId)
        {
            List<int> list = new List<int>();
            list.Add(tagId);

            IImageTag imageTag = m_tagMap[tagId];
            foreach (IImageTag tag in imageTag.ChildTags)
            {
                list.AddRange(GetTagList(tag.Id));
            }

            return list;
        }

        public IImageTag GetTagByPath(string tagPath)
        {
            foreach (IImageTag tag in m_tagMap.Values)
            {
                if (String.Compare(tag.Path, tagPath, true) == 0)
                {
                    return tag;
                }
            }

            return null;
        }

        public IImageTag[] GetTagByName(string tagName, bool partialMatch)
        {
            List<IImageTag> m_tags = new List<IImageTag>();
            foreach (IImageTag tag in m_tagMap.Values)
            {
                if (partialMatch == true)
                {
                    if (tag.Name.Contains(tagName) == true)
                    {
                        m_tags.Add(tag);
                    }
                }
                else
                {
                    if (String.Compare(tag.Name, tagName, true) == 0)
                    {
                        m_tags.Add(tag);
                    }
                }
            }

            return m_tags.ToArray();
        }
    
        public IImageTag[] GetTagByName(string tagName)
        {
            return GetTagByName(tagName, false);
        }

        public IImageTag GetTagByKeyword(string keyword)
        {
            foreach (IImageTag tag in m_tagMap.Values)
            {
                if (String.Compare(tag.Keyword, keyword, true) == 0)
                {
                    return tag;
                }
            }

            return null;
        }
    }
}
